#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>

#include <asm/io.h>
#include <asm/uaccess.h>

#include "fs4412_led.h"    //寄存器物理地址的宏定义，cmd参数的宏定义

MODULE_LICENSE("Dual BSD/GPL");  //版权开源

#define LED_MA 500
#define LED_MI 0
#define LED_NUM 1


/* 定义寄存器指针变量*/
static unsigned int *gpm1con;
static unsigned int *gpm1dat; 
static unsigned int *gpm2con; 
static unsigned int *gpm2dat; 
static unsigned int *gpm3con; 
static unsigned int *gpm3dat;
static unsigned int *gpm4con; 
static unsigned int *gpm4dat;


/* 定义结构体变量*/
struct cdev cdev;



/* 点亮LED灯*/
void fs4412_led_on(int nr)
{
	switch(nr) {
		case 1:
                     writel(readl(gpm4dat) & ~(1<<2),gpm4dat);
		     break;
                case 2:
                     writel(readl(gpm1dat) & ~(0x1<<6),gpm1dat);
		     break;
		case 3:
                     writel(readl(gpm3dat) & ~(0x1<<5),gpm3dat);
		     break;
                case 4:
                     writel(readl(gpm1dat) & ~(0x1<<3),gpm1dat);
		     break; 
		case 5:
                     writel(readl(gpm2dat) & ~(0x1<<2),gpm2dat);
		     break; 
		default:
			break;
	}
}


/* 关闭LED灯*/
void fs4412_led_off(int nr)
{
	switch(nr) {
		case 1:
		     writel(readl(gpm4dat) | (1<<2),gpm4dat);	
		     break;
                case 2:
                     writel(readl(gpm1dat) | (0x1<<6),gpm1dat);
		     break;
		case 3:
                     writel(readl(gpm3dat) | (0x1<<5),gpm3dat);
		     break;
	   	case 4:
                     writel(readl(gpm1dat) | (0x1<<3),gpm1dat);
		     break; 
		case 5:
                     writel(readl(gpm2dat) | (0x1<<2),gpm2dat);
		     break; 
		default:
		     break;
	}
}


/* 与应用程序对应，对应OPEN*/
static int fs4412_led_open(struct inode *inode, struct file *file)
{
	return 0;
}

/* 与应用程序对应，对应退出（Ctrl+C）*/
static int fs4412_led_release(struct inode *inode, struct file *file)
{
	return 0;
}
	
	
	
	
/* 与应用程序对应，对应ioctl*/
static long fs4412_led_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	int nr;

	if(copy_from_user((void *)&nr, (void *)arg, sizeof(nr)))
		return -EFAULT;

	if (nr < 1 || nr > 5)
		return -EINVAL;

	switch (cmd) {
		case LED_ON:
                        fs4412_led_on(nr);
			break;
		case LED_OFF:
                        fs4412_led_off(nr);
			break;
		default:
			printk("Invalid argument");
			return -EINVAL;
	}

	return 0;
}



/* I/O映射，物理地址映射为虚拟地址*/
int fs4412_led_ioremap(void)
{
	int ret;

	gpm1con = ioremap(FS4412_GPM1CON, 4);
	if (gpm1con == NULL) {
		printk("ioremap gpm1con\n");
		ret = -ENOMEM;
		return ret;
	}

	gpm1dat = ioremap(FS4412_GPM1DAT, 4);
	if (gpm1dat == NULL) {
		printk("ioremap gpm1dat\n");
		ret = -ENOMEM;
		return ret;
	}


	gpm2con = ioremap(FS4412_GPM2CON, 4);
	if (gpm2con == NULL) {
		printk("ioremap gpm2con\n");
		ret = -ENOMEM;
		return ret;
	}

	gpm2dat = ioremap(FS4412_GPM2DAT, 4);
	if (gpm2dat == NULL) {
		printk("ioremap gpm2dat\n");
		ret = -ENOMEM;
		return ret;
	}
	gpm3con = ioremap(FS4412_GPM3CON, 4);
	if (gpm3con == NULL) {
		printk("ioremap gpm3con\n");
		ret = -ENOMEM;
		return ret;
	}

	gpm3dat = ioremap(FS4412_GPM3DAT, 4);
	if (gpm3dat == NULL) {
		printk("ioremap gpm3dat\n");
		ret = -ENOMEM;
		return ret;
	}

	gpm4con = ioremap(FS4412_GPM4CON, 4);
	if (gpm4con == NULL) {
		printk("ioremap gpm4con\n");
		ret = -ENOMEM;
		return ret;
	}

	gpm4dat = ioremap(FS4412_GPM4DAT, 4);
	if (gpm4dat == NULL) {
		printk("ioremap gpm4dat\n");
		ret = -ENOMEM;
		return ret;
	}

	
	return 0;
}



/* 释放寄存器地址映射*/
void fs4412_led_iounmap(void)
{
	iounmap(gpm1con);
	iounmap(gpm1dat);
	iounmap(gpm2con);
	iounmap(gpm2dat);
	iounmap(gpm3con);
	iounmap(gpm3dat);
	iounmap(gpm4con);
	iounmap(gpm4dat);
}




/* I/O初始化*/
void fs4412_led_io_init(void)
{
//	GPM4_2 GPM1_6 GPM3_5 GPM1_3 GPM2_2

	writel((readl(gpm4con) & ~(0xf<<8)) | (0x1<<8),gpm4con);

        writel((readl(gpm1con) & ~(0xf<<24)) | (0x1<<24),gpm1con);

        writel((readl(gpm3con) & ~(0xf<<20)) | (0x1<<20),gpm3con);

        writel((readl(gpm1con) & ~(0xf<<12)) | (0x1<<12),gpm1con);

        writel((readl(gpm2con) & ~(0xf<<8)) | (0x1<<8),gpm2con);

}


/* 定位文件操作函数*/
struct file_operations fs4412_led_fops = {
	.owner = THIS_MODULE,
	.open = fs4412_led_open,
	.release = fs4412_led_release,
	.unlocked_ioctl = fs4412_led_unlocked_ioctl,
};


/* 创建字符设备，进行点灯初始化*/
static int fs4412_led_init(void)
{
	dev_t devno = MKDEV(LED_MA, LED_MI);  //由主次设备号获得dev_t的宏
	int ret;

	ret = register_chrdev_region(devno, LED_NUM, "newled"); //并为其分配设备号，为注册设备做准备
	if (ret < 0) {
		printk("register_chrdev_region\n");
		return ret;
	}

	 //注册文件操作字符集
	cdev_init(&cdev, &fs4412_led_fops);   //初始化，建立cdev和file_operation 之间的连接
	cdev.owner = THIS_MODULE;
	ret = cdev_add(&cdev, devno, LED_NUM); //注册设备····
	if (ret < 0) {
		printk("cdev_add\n");
		goto err1;
	}

	ret = fs4412_led_ioremap();   //将控制LED灯的寄存器物理地址映射为虚拟地址
	if (ret < 0)
		goto err2;


	fs4412_led_io_init();

	printk("Led init\n");

	return 0;
err2:
	cdev_del(&cdev);  //注销设备，对应cdev_add
err1:
	unregister_chrdev_region(devno, LED_NUM);  //释放之前申请的设备号，对应register_chrdev_region
	return ret;
}




static void fs4412_led_exit(void)   //资源释放函数
{
	dev_t devno = MKDEV(LED_MA, LED_MI);

	fs4412_led_iounmap(); //取消地址映射，对应fs4412_led_init()中的fs4412_led_ioremap()
	cdev_del(&cdev); //注销设备  对应fs4412_led_init()中的cdev_add（）
	unregister_chrdev_region(devno, LED_NUM); //释放fs4412_led_init()中通过register_chrdev_region（）注册的设备号
	printk("Led exit\n");
}

module_init(fs4412_led_init);
module_exit(fs4412_led_exit);
